/*
 * jQuery UI Draggable 1.6
 *
 * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Draggables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

    $.widget("ui.draggable", $.extend({}, $.ui.mouse, {
        _init: function() {

            if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
                this.element[0].style.position = 'relative';

            (this.options.cssNamespace && this.element.addClass(this.options.cssNamespace + "-draggable"));
            (this.options.disabled && this.element.addClass('ui-draggable-disabled'));

            this._mouseInit();

        },
        destroy: function() {
            if (!this.element.data('draggable'))
                return;
            this.element.removeData("draggable").unbind(".draggable").removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled');
            this._mouseDestroy();
        },
        _mouseCapture: function(event) {

            var o = this.options;

            if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
                return false;

            //Quit if we're not on a valid handle
            this.handle = this._getHandle(event);
            if (!this.handle)
                return false;

            return true;

        },
        _mouseStart: function(event) {

            var o = this.options;

            //Create and append the visible helper
            this.helper = this._createHelper(event);

            //Cache the helper size
            this._cacheHelperProportions();

            //If ddmanager is used for droppables, set the global draggable
            if ($.ui.ddmanager)
                $.ui.ddmanager.current = this;

            /*
             * - Position generation -
             * This block generates everything position related - it's the core of draggables.
             */

            //Cache the margins of the original element
            this._cacheMargins();

            //Store the helper's css position
            this.cssPosition = this.helper.css("position");
            this.scrollParent = this.helper.scrollParent();

            //The element's absolute position on the page minus margins
            this.offset = this.element.offset();
            this.offset = {
                top: this.offset.top - this.margins.top,
                left: this.offset.left - this.margins.left
            };

            $.extend(this.offset, {
                click: {//Where the click happened, relative to the element
                    left: event.pageX - this.offset.left,
                    top: event.pageY - this.offset.top
                },
                parent: this._getParentOffset(),
                relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
            });

            //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
            if (o.cursorAt)
                this._adjustOffsetFromHelper(o.cursorAt);

            //Generate the original position
            this.originalPosition = this._generatePosition(event);

            //Set a containment if given in the options
            if (o.containment)
                this._setContainment();

            //Call plugins and callbacks
            this._propagate("start", event);

            //Recache the helper size
            this._cacheHelperProportions();

            //Prepare the droppable offsets
            if ($.ui.ddmanager && !o.dropBehaviour)
                $.ui.ddmanager.prepareOffsets(this, event);

            this.helper.addClass("ui-draggable-dragging");
            this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
            return true;
        },
        _mouseDrag: function(event, noPropagation) {

            //Compute the helpers position
            this.position = this._generatePosition(event);
            this.positionAbs = this._convertPositionTo("absolute");

            //Call plugins and callbacks and use the resulting position if something is returned
            if (!noPropagation)
                this.position = this._propagate("drag", event) || this.position;

            if (!this.options.axis || this.options.axis != "y")
                this.helper[0].style.left = this.position.left + 'px';
            if (!this.options.axis || this.options.axis != "x")
                this.helper[0].style.top = this.position.top + 'px';
            if ($.ui.ddmanager)
                $.ui.ddmanager.drag(this, event);

            return false;
        },
        _mouseStop: function(event) {

            //If we are using droppables, inform the manager about the drop
            var dropped = false;
            if ($.ui.ddmanager && !this.options.dropBehaviour)
                var dropped = $.ui.ddmanager.drop(this, event);

            if ((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
                var self = this;
                $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
                    self._propagate("stop", event);
                    self._clear();
                });
            } else {
                this._propagate("stop", event);
                this._clear();
            }

            return false;
        },
        _getHandle: function(event) {

            var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
            $(this.options.handle, this.element)
                    .find("*")
                    .andSelf()
                    .each(function() {
                if (this == event.target)
                    handle = true;
            });

            return handle;

        },
        _createHelper: function(event) {

            var o = this.options;
            var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);

            if (!helper.parents('body').length)
                helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));

            if (helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
                helper.css("position", "absolute");

            return helper;

        },
        _adjustOffsetFromHelper: function(obj) {
            if (obj.left != undefined)
                this.offset.click.left = obj.left + this.margins.left;
            if (obj.right != undefined)
                this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
            if (obj.top != undefined)
                this.offset.click.top = obj.top + this.margins.top;
            if (obj.bottom != undefined)
                this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
        },
        _getParentOffset: function() {

            this.offsetParent = this.helper.offsetParent();
            var po = this.offsetParent.offset();			//Get the offsetParent and cache its position

            if ((this.offsetParent[0] == document.body && $.browser.mozilla)	//Ugly FF3 fix
                    || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
                po = {top: 0, left: 0};

            return {
                top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
                left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
            };

        },
        _getRelativeOffset: function() {

            if (this.cssPosition == "relative") {
                var p = this.element.position();
                return {
                    top: p.top - (parseInt(this.helper.css("top"), 10) || 0) + this.scrollParent.scrollTop(),
                    left: p.left - (parseInt(this.helper.css("left"), 10) || 0) + this.scrollParent.scrollLeft()
                };
            } else {
                return {top: 0, left: 0};
            }

        },
        _cacheMargins: function() {
            this.margins = {
                left: (parseInt(this.element.css("marginLeft"), 10) || 0),
                top: (parseInt(this.element.css("marginTop"), 10) || 0)
            };
        },
        _cacheHelperProportions: function() {
            this.helperProportions = {
                width: this.helper.outerWidth(),
                height: this.helper.outerHeight()
            };
        },
        _setContainment: function() {

            var o = this.options;
            if (o.containment == 'parent')
                o.containment = this.helper[0].parentNode;
            if (o.containment == 'document' || o.containment == 'window')
                this.containment = [
                    0 - this.offset.relative.left - this.offset.parent.left,
                    0 - this.offset.relative.top - this.offset.parent.top,
                    $(o.containment == 'document' ? document : window).width() - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.element.css("marginRight"), 10) || 0),
                    ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.element.css("marginBottom"), 10) || 0)
                ];

            if (!(/^(document|window|parent)$/).test(o.containment)) {
                var ce = $(o.containment)[0];
                var co = $(o.containment).offset();
                var over = ($(ce).css("overflow") != 'hidden');

                this.containment = [
                    co.left + (parseInt($(ce).css("borderLeftWidth"), 10) || 0) - this.offset.relative.left - this.offset.parent.left - this.margins.left,
                    co.top + (parseInt($(ce).css("borderTopWidth"), 10) || 0) - this.offset.relative.top - this.offset.parent.top - this.margins.top,
                    co.left + (over ? Math.max(ce.scrollWidth, ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"), 10) || 0) - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left,
                    co.top + (over ? Math.max(ce.scrollHeight, ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"), 10) || 0) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top
                ];
            }

        },
        _convertPositionTo: function(d, pos) {

            if (!pos)
                pos = this.position;
            var mod = d == "absolute" ? 1 : -1;
            var scroll = this[(this.cssPosition == 'absolute' ? 'offset' : 'scroll') + 'Parent'], scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);

            return {
                top: (
                        pos.top																	// the calculated relative position
                        + this.offset.relative.top * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
                        + this.offset.parent.top * mod											// The offsetParent's offset without borders (offset + border)
                        + (this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : (scrollIsRootNode ? 0 : scroll.scrollTop())) * mod
                        + this.margins.top * mod												//Add the margin (you don't want the margin counting in intersection methods)
                        ),
                left: (
                        pos.left																// the calculated relative position
                        + this.offset.relative.left * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
                        + this.offset.parent.left * mod											// The offsetParent's offset without borders (offset + border)
                        + (this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : (scrollIsRootNode ? 0 : scroll.scrollLeft())) * mod
                        + this.margins.left * mod												//Add the margin (you don't want the margin counting in intersection methods)
                        )
            };
        },
        _generatePosition: function(event) {

            var o = this.options, scroll = this[(this.cssPosition == 'absolute' ? 'offset' : 'scroll') + 'Parent'], scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);

            var position = {
                top: (
                        event.pageY																// The absolute mouse position
                        - this.offset.click.top													// Click offset (relative to the element)
                        - this.offset.relative.top												// Only for relative positioned nodes: Relative offset from element to offset parent
                        - this.offset.parent.top												// The offsetParent's offset without borders (offset + border)
                        + (this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : (scrollIsRootNode ? 0 : scroll.scrollTop()))
                        ),
                left: (
                        event.pageX																// The absolute mouse position
                        - this.offset.click.left												// Click offset (relative to the element)
                        - this.offset.relative.left												// Only for relative positioned nodes: Relative offset from element to offset parent
                        - this.offset.parent.left												// The offsetParent's offset without borders (offset + border)
                        + (this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft())
                        )
            };

            if (!this.originalPosition)
                return position;										//If we are not dragging yet, we won't check for options

            /*
             * - Position constraining -
             * Constrain the position to a mix of grid, containment.
             */
            if (this.containment) {
                if (position.left < this.containment[0])
                    position.left = this.containment[0];
                if (position.top < this.containment[1])
                    position.top = this.containment[1];
                if (position.left > this.containment[2])
                    position.left = this.containment[2];
                if (position.top > this.containment[3])
                    position.top = this.containment[3];
            }

            if (o.grid) {
                var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1];
                position.top = this.containment ? (!(top < this.containment[1] || top > this.containment[3]) ? top : (!(top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;

                var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0];
                position.left = this.containment ? (!(left < this.containment[0] || left > this.containment[2]) ? left : (!(left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
            }

            return position;
        },
        _clear: function() {
            this.helper.removeClass("ui-draggable-dragging");
            if (this.helper[0] != this.element[0] && !this.cancelHelperRemoval)
                this.helper.remove();
            //if($.ui.ddmanager) $.ui.ddmanager.current = null;
            this.helper = null;
            this.cancelHelperRemoval = false;
        },
        // From now on bulk stuff - mainly helpers

        _propagate: function(n, event) {
            $.ui.plugin.call(this, n, [event, this._uiHash()]);
            if (n == "drag")
                this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
            return this.element.triggerHandler(n == "drag" ? n : "drag" + n, [event, this._uiHash()], this.options[n]);
        },
        plugins: {},
        _uiHash: function(event) {
            return {
                helper: this.helper,
                position: this.position,
                absolutePosition: this.positionAbs,
                options: this.options
            };
        }

    }));

    $.extend($.ui.draggable, {
        version: "1.6",
        defaults: {
            appendTo: "parent",
            axis: false,
            cancel: ":input",
            connectToSortable: false,
            containment: false,
            cssNamespace: "ui",
            cursor: "default",
            cursorAt: null,
            delay: 0,
            distance: 1,
            grid: false,
            handle: false,
            helper: "original",
            iframeFix: false,
            opacity: 1,
            refreshPositions: false,
            revert: false,
            revertDuration: 500,
            scope: "default",
            scroll: true,
            scrollSensitivity: 20,
            scrollSpeed: 20,
            snap: false,
            snapMode: "both",
            snapTolerance: 20,
            stack: false,
            zIndex: null
        }
    });

    $.ui.plugin.add("draggable", "connectToSortable", {
        start: function(event, ui) {

            var inst = $(this).data("draggable");
            inst.sortables = [];
            $(ui.options.connectToSortable).each(function() {
                // 'this' points to a string, and should therefore resolved as query, but instead, if the string is assigned to a variable, it loops through the strings properties,
                // so we have to append '' to make it anonymous again
                $(this + '').each(function() {
                    if ($.data(this, 'sortable')) {
                        var sortable = $.data(this, 'sortable');
                        inst.sortables.push({
                            instance: sortable,
                            shouldRevert: sortable.options.revert
                        });
                        sortable._refreshItems();	//Do a one-time refresh at start to refresh the containerCache
                        sortable._propagate("activate", event, inst);
                    }
                });
            });

        },
        stop: function(event, ui) {

            //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
            var inst = $(this).data("draggable");

            $.each(inst.sortables, function() {
                if (this.instance.isOver) {
                    this.instance.isOver = 0;
                    inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
                    this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
                    if (this.shouldRevert)
                        this.instance.options.revert = true; //revert here
                    this.instance._mouseStop(event);

                    //Also propagate receive event, since the sortable is actually receiving a element
                    this.instance.element.triggerHandler("sortreceive", [event, $.extend(this.instance._ui(), {sender: inst.element})], this.instance.options["receive"]);

                    this.instance.options.helper = this.instance.options._helper;

                    if (inst.options.helper == 'original') {
                        this.instance.currentItem.css({top: 'auto', left: 'auto'});
                    }

                } else {
                    this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
                    this.instance._propagate("deactivate", event, inst);
                }

            });

        },
        drag: function(event, ui) {

            var inst = $(this).data("draggable"), self = this;

            var checkPos = function(o) {
                var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
                var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
                var itemHeight = o.height, itemWidth = o.width;
                var itemTop = o.top, itemLeft = o.left;

                return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
            };

            $.each(inst.sortables, function(i) {

                if (checkPos.call(inst, this.instance.containerCache)) {

                    //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
                    if (!this.instance.isOver) {
                        this.instance.isOver = 1;
                        //Now we fake the start of dragging for the sortable instance,
                        //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
                        //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
                        this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
                        this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
                        this.instance.options.helper = function() {
                            return ui.helper[0];
                        };

                        event.target = this.instance.currentItem[0];
                        this.instance._mouseCapture(event, true);
                        this.instance._mouseStart(event, true, true);

                        //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
                        this.instance.offset.click.top = inst.offset.click.top;
                        this.instance.offset.click.left = inst.offset.click.left;
                        this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
                        this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;

                        inst._propagate("toSortable", event);

                    }

                    //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
                    if (this.instance.currentItem)
                        this.instance._mouseDrag(event);

                } else {

                    //If it doesn't intersect with the sortable, and it intersected before,
                    //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
                    if (this.instance.isOver) {
                        this.instance.isOver = 0;
                        this.instance.cancelHelperRemoval = true;
                        this.instance.options.revert = false; //No revert here
                        this.instance._mouseStop(event, true);
                        this.instance.options.helper = this.instance.options._helper;

                        //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
                        this.instance.currentItem.remove();
                        if (this.instance.placeholder)
                            this.instance.placeholder.remove();

                        inst._propagate("fromSortable", event);
                    }

                }
                ;

            });

        }
    });

    $.ui.plugin.add("draggable", "cursor", {
        start: function(event, ui) {
            var t = $('body');
            if (t.css("cursor"))
                ui.options._cursor = t.css("cursor");
            t.css("cursor", ui.options.cursor);
        },
        stop: function(event, ui) {
            if (ui.options._cursor)
                $('body').css("cursor", ui.options._cursor);
        }
    });

    $.ui.plugin.add("draggable", "iframeFix", {
        start: function(event, ui) {
            $(ui.options.iframeFix === true ? "iframe" : ui.options.iframeFix).each(function() {
                $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
                        .css({
                    width: this.offsetWidth + "px", height: this.offsetHeight + "px",
                    position: "absolute", opacity: "0.001", zIndex: 1000
                })
                        .css($(this).offset())
                        .appendTo("body");
            });
        },
        stop: function(event, ui) {
            $("div.ui-draggable-iframeFix").each(function() {
                this.parentNode.removeChild(this);
            }); //Remove frame helpers
        }
    });

    $.ui.plugin.add("draggable", "opacity", {
        start: function(event, ui) {
            var t = $(ui.helper);
            if (t.css("opacity"))
                ui.options._opacity = t.css("opacity");
            t.css('opacity', ui.options.opacity);
        },
        stop: function(event, ui) {
            if (ui.options._opacity)
                $(ui.helper).css('opacity', ui.options._opacity);
        }
    });

    $.ui.plugin.add("draggable", "scroll", {
        start: function(event, ui) {
            var o = ui.options;
            var i = $(this).data("draggable");

            if (i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML')
                i.overflowOffset = i.scrollParent.offset();

        },
        drag: function(event, ui) {

            var o = ui.options, scrolled = false;
            var i = $(this).data("draggable");

            if (i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {

                if ((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
                    i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
                else if (event.pageY - i.overflowOffset.top < o.scrollSensitivity)
                    i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;

                if ((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
                    i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
                else if (event.pageX - i.overflowOffset.left < o.scrollSensitivity)
                    i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;

            } else {

                if (event.pageY - $(document).scrollTop() < o.scrollSensitivity)
                    scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
                else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
                    scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);

                if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
                    scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
                else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
                    scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);

            }

            if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
                $.ui.ddmanager.prepareOffsets(i, event);



            // This is a special case where we need to modify a offset calculated on start, since the following happened:
            // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
            // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
            //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
            if (scrolled !== false && i.cssPosition == 'absolute' && i.scrollParent[0] != document && $.ui.contains(i.scrollParent[0], i.offsetParent[0])) {
                i.offset.parent = i._getParentOffset();

            }

            // This is another very weird special case that only happens for relative elements:
            // 1. If the css position is relative
            // 2. and the scroll parent is the document or similar to the offset parent
            // we have to refresh the relative offset during the scroll so there are no jumps
            if (scrolled !== false && i.cssPosition == 'relative' && !(i.scrollParent[0] != document && i.scrollParent[0] != i.offsetParent[0])) {
                i.offset.relative = i._getRelativeOffset();
            }


        }
    });

    $.ui.plugin.add("draggable", "snap", {
        start: function(event, ui) {

            var inst = $(this).data("draggable");
            inst.snapElements = [];

            $(ui.options.snap.constructor != String ? (ui.options.snap.items || ':data(draggable)') : ui.options.snap).each(function() {
                var $t = $(this);
                var $o = $t.offset();
                if (this != inst.element[0])
                    inst.snapElements.push({
                        item: this,
                        width: $t.outerWidth(), height: $t.outerHeight(),
                        top: $o.top, left: $o.left
                    });
            });

        },
        drag: function(event, ui) {

            var inst = $(this).data("draggable");
            var d = ui.options.snapTolerance;

            var x1 = ui.absolutePosition.left, x2 = x1 + inst.helperProportions.width,
                    y1 = ui.absolutePosition.top, y2 = y1 + inst.helperProportions.height;

            for (var i = inst.snapElements.length - 1; i >= 0; i--) {

                var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
                        t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;

                //Yes, I know, this is insane ;)
                if (!((l - d < x1 && x1 < r + d && t - d < y1 && y1 < b + d) || (l - d < x1 && x1 < r + d && t - d < y2 && y2 < b + d) || (l - d < x2 && x2 < r + d && t - d < y1 && y1 < b + d) || (l - d < x2 && x2 < r + d && t - d < y2 && y2 < b + d))) {
                    if (inst.snapElements[i].snapping)
                        (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), {snapItem: inst.snapElements[i].item})));
                    inst.snapElements[i].snapping = false;
                    continue;
                }

                if (ui.options.snapMode != 'inner') {
                    var ts = Math.abs(t - y2) <= d;
                    var bs = Math.abs(b - y1) <= d;
                    var ls = Math.abs(l - x2) <= d;
                    var rs = Math.abs(r - x1) <= d;
                    if (ts)
                        ui.position.top = inst._convertPositionTo("relative", {top: t - inst.helperProportions.height, left: 0}).top;
                    if (bs)
                        ui.position.top = inst._convertPositionTo("relative", {top: b, left: 0}).top;
                    if (ls)
                        ui.position.left = inst._convertPositionTo("relative", {top: 0, left: l - inst.helperProportions.width}).left;
                    if (rs)
                        ui.position.left = inst._convertPositionTo("relative", {top: 0, left: r}).left;
                }

                var first = (ts || bs || ls || rs);

                if (ui.options.snapMode != 'outer') {
                    var ts = Math.abs(t - y1) <= d;
                    var bs = Math.abs(b - y2) <= d;
                    var ls = Math.abs(l - x1) <= d;
                    var rs = Math.abs(r - x2) <= d;
                    if (ts)
                        ui.position.top = inst._convertPositionTo("relative", {top: t, left: 0}).top;
                    if (bs)
                        ui.position.top = inst._convertPositionTo("relative", {top: b - inst.helperProportions.height, left: 0}).top;
                    if (ls)
                        ui.position.left = inst._convertPositionTo("relative", {top: 0, left: l}).left;
                    if (rs)
                        ui.position.left = inst._convertPositionTo("relative", {top: 0, left: r - inst.helperProportions.width}).left;
                }

                if (!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
                    (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), {snapItem: inst.snapElements[i].item})));
                inst.snapElements[i].snapping = (ts || bs || ls || rs || first);

            }
            ;

        }
    });

    $.ui.plugin.add("draggable", "stack", {
        start: function(event, ui) {
            var group = $.makeArray($(ui.options.stack.group)).sort(function(a, b) {
                return (parseInt($(a).css("zIndex"), 10) || ui.options.stack.min) - (parseInt($(b).css("zIndex"), 10) || ui.options.stack.min);
            });

            $(group).each(function(i) {
                this.style.zIndex = ui.options.stack.min + i;
            });

            this[0].style.zIndex = ui.options.stack.min + group.length;
        }
    });

    $.ui.plugin.add("draggable", "zIndex", {
        start: function(event, ui) {
            var t = $(ui.helper);
            if (t.css("zIndex"))
                ui.options._zIndex = t.css("zIndex");
            t.css('zIndex', ui.options.zIndex);
        },
        stop: function(event, ui) {
            if (ui.options._zIndex)
                $(ui.helper).css('zIndex', ui.options._zIndex);
        }
    });

})(jQuery);
